import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Collection;
import java.util.ArrayList;

public abstract class Course {

	private String lvanr = "";
	private String title = "";
	private GregorianCalendar enrollStart = null;
	private GregorianCalendar enrollEnd = null;
	private GregorianCalendar checkOutEnd = null;
	private boolean visible = false;
	private double ects = 0;
	private double workinghours = 0;
	private Professor professor = null;
	private ArrayList<Course> requirements = null;
	private HashMap<String, Student> enrolledStudents = null;
	private int maxStudents = 0;

	/**Creates a new course at university with all its basic information.
	 * 
	 * @param lvanr The individual number as technical description for the course.
	 * @param title The title of the course
	 * @param enrollStart   The day from which on Students can enroll into the course.
	 * @param enrollEnd The day when the period for Students to enroll into the course ends.
	 * @param checkOutEnd   The last possible day for Students to leave the course.
	 * @param visible Sets a boolean flag, if the Course is officialy vissible or not.
	 */
	public Course(String lvanr, String title, double ects, double workinghours, GregorianCalendar enrollStart, GregorianCalendar enrollEnd, GregorianCalendar checkOutEnd, Professor professor, boolean visible) {
		this.lvanr = lvanr;
		this.title = title;
		this.ects = ects;
		this.workinghours = workinghours;
		this.enrollStart = enrollStart;
		this.enrollEnd = enrollEnd;
		this.checkOutEnd = checkOutEnd;
		this.professor = professor;
		this.visible = visible;
		this.enrolledStudents = new HashMap<String, Student>();
		this.requirements = new ArrayList<Course>();
	}

	public Course(String lvanr, String title, double ects, double workinghours, GregorianCalendar enrollStart, GregorianCalendar enrollEnd, GregorianCalendar checkOutEnd, Professor professor, boolean visible, int maxStudents) {
		this.lvanr = lvanr;
		this.title = title;
		this.ects = ects;
		this.workinghours = workinghours;
		this.enrollStart = enrollStart;
		this.enrollEnd = enrollEnd;
		this.checkOutEnd = checkOutEnd;
		this.professor = professor;
		this.visible = visible;
		this.enrolledStudents = new HashMap<String, Student>();
		this.requirements = new ArrayList<Course>();
		this.maxStudents = maxStudents;
	}

	/**Creates a new course at university with all its basic information.
	 * 
	 * @param lvanr The individual number as technical description for the course.
	 * @param title The title of the course
	 * @param enrollStart   The day from which on Students can enroll into the course.
	 * @param enrollEnd The day when the period for Students to enroll into the course ends.
	 * @param checkOutEnd   The last possible day for Students to leave the course.  
	 */
	public Course(String lvanr, String title, double ects, double workinghours, GregorianCalendar enrollStart, GregorianCalendar enrollEnd, GregorianCalendar checkOutEnd, Professor professor) {
		this.lvanr = lvanr;
		this.title = title;
		this.ects = ects;
		this.workinghours = workinghours;
		this.enrollStart = enrollStart;
		this.enrollEnd = enrollEnd;
		this.checkOutEnd = checkOutEnd;
		this.professor = professor;
		this.enrolledStudents = new HashMap<String, Student>();
		this.requirements = new ArrayList<Course>();
	}

	public abstract void giveCertificate(Student student);
	
	/**Enrolls a new Student into the course if the date is not too early or too late. Students cannot enroll double into a course.
	 * 
	 * @param student The Student that will be enrolled into the course.
	 * @return True if the Student was sucessfully enrolled, false otherwise.
	 */
	public boolean enroll(Student student) {
		if (this.enrollStart.before(new GregorianCalendar()) && this.enrollEnd.after(new GregorianCalendar()) && !this.enrolledStudents.containsKey(student.getMatnr()) && this.checkRequirements(student)) {
			if (maxStudents != 0 && enrolledStudents.size() + 1 > maxStudents) {
				return false;
			}
			enrolledStudents.put(student.getMatnr(), student);
			return true;
		}
		return false;
	}

	/**Checks out a Student of the course, if the date is not too late. Student has to be enrolled into the course to be checked out.
	 * 
	 * @param student The Student that wants to leave the course.
	 * @return True if the Student was sucessfully checked out of the course, false otherwise.
	 */
	public boolean checkOut(Student student) {
		if (this.checkOutEnd.before(new GregorianCalendar())) {
			return false;
		}
		if (enrolledStudents.remove(student.getMatnr()) == null) {
			return false;
		}
		return true;
	}

	public void cancel() {
		this.setVisible(false);
		Student temp[] = new Student[this.enrolledStudents.size()];
		Collection<Student> a = this.enrolledStudents.values();
		temp = a.toArray(temp);
		for (int i = 0; i < temp.length; i++) {
			temp[i].SendMessage(this.getLvanr(), this.getTitle() + " was cancelled");
		}
	}

	public void addRequirements(Course course) {
		this.requirements.add(course);
	}

	/**Returns the date from with on Students can enroll into the course.
	 * 
	 * @return A Date-representation of the date from with on Students can enroll into the course.
	 */
	public GregorianCalendar getEnrollStart() {
		return this.enrollStart;
	}

	public void setCheckOutEnd(GregorianCalendar cal) {
		this.checkOutEnd = cal;
	}

	/**Returns the date from which on Students can no longer check out of the course before it officially starts.
	 * 
	 * @return A Date-representation of the date from which on students can no longer check out of the course.
	 */
	public GregorianCalendar getCheckOutEnd() {
		return this.checkOutEnd;
	}

	public Professor getProfessor() {
		return this.professor;
	}

	/**Returns the number of Students who are currently enrolled into the course. 
	 * 
	 * @return A Integer-representation of the number of Students enrolled into the specified course.
	 */
	public int getStudentsCount() {
		return this.enrolledStudents.size();
	}

	/**Returns an detailled overview of all Students currently enrolled into the course.
	 * 
	 * @return A HashMap containing all Students currently enrolled into the course. Student's Matrikelnummer as key. 
	 */
	public HashMap<String, Student> getStudents() {
		return enrolledStudents;
	}

	private String getForamtedDate(GregorianCalendar cal) {
		return cal.get(Calendar.DAY_OF_MONTH) + "." + cal.get(Calendar.MONTH) + "." + cal.get(Calendar.YEAR) + " " + cal.get(Calendar.HOUR) + ":" + cal.get(Calendar.MINUTE);
	}

	private boolean checkRequirements(Student student) {
		boolean check = true;
		boolean found = false;
		for (int i = 0; i < this.requirements.size(); i++) {
			for (int j = 0; j < student.getCertificates().size(); j++) {
				if (this.requirements.get(i).equals(student.getCertificates().get(i).getCourse()) && student.getCertificates().get(i).getMark().getMark() < 5) {
					found = true;
				}
			}
			if (!found) {
				check = false;
			}
			found = false;
		}
		return check;
	}

	public String Requirements() {
		String temp = "";
		for (int i = 0; i < requirements.size(); i++) {
			if (i == requirements.size() - 1) {
				temp += requirements.get(i).toString();
			} else {
				temp += requirements.get(i).toString() + '\n';
			}
		}
		return temp;
	}

	public void setEnrollEnd(GregorianCalendar cal) {
		this.enrollEnd = cal;
	}

	/**Returns the date from which on Students can no longer enroll into the course.
	 * 
	 * @return A Date-representation of the day from which on it is no longer possible to enroll into the course.
	 */
	public GregorianCalendar getEnrollEnd() {
		return this.enrollEnd;
	}

	/**Returns the course's technical numeric description.
	 * 
	 * @return A String-representation of the course's numeric description.
	 */
	public String getLvanr() {
		return this.lvanr;
	}

	/**Returns the name of the course. 
	 * 
	 * @return A String-representation of the course's name.
	 */
	public String getTitle() {
		return this.title;
	}

	public double getEcts() {
		return this.ects;
	}

	public double getWorkingHours() {
		return this.workinghours;
	}

	public void setEnrollStart(GregorianCalendar cal) {
		this.enrollStart = cal;
	}

	/**
	 * @return the maxStudents
	 */
	public int getMaxStudents() {
		return maxStudents;
	}

	/**
	 * @param maxStudents the maxStudents to set
	 */
	public void setMaxStudents(int maxStudents) {
		this.maxStudents = maxStudents;
	}

	public void setVisible(boolean visible) {
		this.visible = visible;
	}

	public boolean getVisible() {
		return visible;
	}

	/**Returns all basic information about the course formatted.
	 * 
	 * @return A String-representation of the course's basic information: number, name and dates.
	 */
	@Override
	public String toString() {
		if (maxStudents != 0) {
			return this.getLvanr() + " " + this.getTitle() + " " + this.getEcts() + " " + this.getWorkingHours() + " " + this.getForamtedDate(this.getEnrollStart()) + " " + this.getForamtedDate(this.getEnrollEnd()) + " " + this.getForamtedDate(this.getCheckOutEnd()) + " " + this.getStudentsCount() + "/" + this.getMaxStudents() + " : " + this.getProfessor();
		}
		return this.getLvanr() + " " + this.getTitle() + " " + this.getEcts() + " " + this.getWorkingHours() + " " + this.getForamtedDate(this.getEnrollStart()) + " " + this.getForamtedDate(this.getEnrollEnd()) + " " + this.getForamtedDate(this.getCheckOutEnd()) + " " + this.getStudentsCount() + " : " + this.getProfessor();
	}
}